#include "uart.h"
#include "board.h"
uartStruct_t devUART3;
static uint8_t buffer[UART3_BUF_SIZE] = {0};
static uint8_t *pbuffer = NULL;
static uint16_t recvLength = 0;

static void UART3_SendBytes(uint8_t *pBuf, uint32_t length);
static void UART3_PrintString(const char *fmt, ...);
static uint16_t UART3_ReadBytes(uint8_t *pBuf);
void USART3_Init(uint32_t BaudRate) {
	LL_RCC_ClocksTypeDef rcc_clocks;
	/* (1) Enable GPIO clock and configures the USART pins **********************/

	/* Enable the peripheral clock of GPIO Port */
	LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOD);
	/* Configure Tx Pin as : Alternate function, High Speed, Push pull, Pull up */
	LL_GPIO_SetPinMode(GPIOD, LL_GPIO_PIN_8, LL_GPIO_MODE_ALTERNATE);
	LL_GPIO_SetPinSpeed(GPIOD, LL_GPIO_PIN_8, LL_GPIO_SPEED_FREQ_HIGH);
	LL_GPIO_SetPinOutputType(GPIOD, LL_GPIO_PIN_8, LL_GPIO_OUTPUT_PUSHPULL);
	LL_GPIO_SetPinPull(GPIOD, LL_GPIO_PIN_8, LL_GPIO_PULL_UP);
	LL_GPIO_SetAFPin_8_15(GPIOD, LL_GPIO_PIN_8, LL_GPIO_AF_7);

	/* Configure Rx Pin as : Alternate function, High Speed, Push pull, Pull up */
	LL_GPIO_SetPinMode(GPIOD, LL_GPIO_PIN_9, LL_GPIO_MODE_ALTERNATE);
	LL_GPIO_SetPinSpeed(GPIOD, LL_GPIO_PIN_9, LL_GPIO_SPEED_FREQ_HIGH);
	LL_GPIO_SetPinOutputType(GPIOD, LL_GPIO_PIN_9, LL_GPIO_OUTPUT_PUSHPULL);
	LL_GPIO_SetPinPull(GPIOD, LL_GPIO_PIN_9, LL_GPIO_PULL_UP);
	LL_GPIO_SetAFPin_8_15(GPIOD, LL_GPIO_PIN_9, LL_GPIO_AF_7);

	/* (2) Enable USART3 peripheral clock and clock source ****************/
	LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART3);
	

	/* (3) Configure USART3 functional parameters ********************************/

	/* TX/RX direction */
	LL_USART_SetTransferDirection(USART3, LL_USART_DIRECTION_TX_RX);
	/* 8 data bit, 1 start bit, 1 stop bit, no parity */
	LL_USART_ConfigCharacter(USART3, LL_USART_DATAWIDTH_8B, LL_USART_PARITY_NONE, LL_USART_STOPBITS_1);
	LL_USART_SetOverSampling(USART3, LL_USART_OVERSAMPLING_16);
	LL_USART_SetHWFlowCtrl(USART3, LL_USART_HWCONTROL_NONE);
	LL_RCC_GetSystemClocksFreq(&rcc_clocks);
	LL_USART_SetBaudRate(USART3, rcc_clocks.PCLK1_Frequency, LL_USART_OVERSAMPLING_16, BaudRate); 
	
	NVIC_SetPriority(USART3_IRQn, 2);  
	NVIC_EnableIRQ(USART3_IRQn);
	LL_USART_EnableIT_IDLE(USART3);
	LL_USART_EnableIT_RXNE(USART3);
	/* (4) Enable USART3 **********************************************************/
	LL_USART_ConfigAsyncMode(USART3);
	LL_USART_Enable(USART3);
	recvLength = 0;
	memset(buffer, 0, sizeof(buffer));
	pbuffer = buffer;
	devUART3.recvFlag = false;
	devUART3.write = UART3_SendBytes;
	devUART3.read = UART3_ReadBytes;
	devUART3.print = UART3_PrintString;
}

static void UART3_SendBytes(uint8_t *pBuf, uint32_t length) {
	uint32_t i;
	for(i=0;i<length;i++) {
		LL_USART_TransmitData8(USART3, pBuf[i]);
		while(LL_USART_IsActiveFlag_TXE(USART3) == RESET);
	}
}

static void UART3_PrintString(const char *fmt, ...) {
	uint16_t i;
	va_list argptr;
	va_start(argptr, fmt);
	memset(uartShareBuffer, 0, sizeof(uartShareBuffer));
	vsprintf((char*)uartShareBuffer, fmt, argptr);
	va_end(argptr);
	for(i=0;i<strlen((char*)uartShareBuffer);i++) {
		if(uartShareBuffer[i] == '\n') {
			LL_USART_TransmitData8(USART3, '\r');
			while(LL_USART_IsActiveFlag_TXE(USART3) == RESET);
		}
		LL_USART_TransmitData8(USART3, uartShareBuffer[i]);
		while(LL_USART_IsActiveFlag_TXE(USART3) == RESET);
	}
}

static uint16_t UART3_ReadBytes(uint8_t *pBuf) {
	memcpy(pBuf, buffer, recvLength);
	devUART3.recvFlag = false;
	return recvLength;
}

static void UART3_Receive_Callback(void) {
	*pbuffer++ = LL_USART_ReceiveData8(USART3);
}

static void UART3_Idle_Callback(void) {
	recvLength = pbuffer - buffer;
	pbuffer = buffer;
	devUART3.recvFlag = true;
}

void USART3_IRQHandler(void) {
	if(LL_USART_IsActiveFlag_RXNE(USART3)) {
		LL_USART_ClearFlag_RXNE(USART3);
		UART3_Receive_Callback();
	}
	else if(LL_USART_IsActiveFlag_IDLE(USART3)) {
		LL_USART_ClearFlag_IDLE(USART3);
		UART3_Idle_Callback();
	}
}

int fputc(int ch, FILE *f) {
	if(((char)ch == '\n')) {
		LL_USART_TransmitData8(USART3, '\r');
		while(LL_USART_IsActiveFlag_TXE(USART3) == RESET);
	}
	LL_USART_TransmitData8(USART3, (char)ch);
	while(LL_USART_IsActiveFlag_TXE(USART3) == RESET);
	return ch;
}

int getc(FILE *f) {
	while(LL_USART_IsActiveFlag_RXNE(USART3)==RESET);
	return (int)LL_USART_ReceiveData8(USART3);
}
